package com.jumi.microservice.service.impl;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.jm.member.common.core.exception.BaseException;
import com.jm.member.common.core.utils.ServletUtils;
import com.jm.member.common.jwt.constant.JwtAdminInfo;
import com.jm.member.common.redis.service.RedisService;
import com.jumi.microservice.aspectj.AdminChangeUserLogAspect;
import com.jumi.microservice.common.base.dto.ResponseResult;
import com.jumi.microservice.datasource.annotation.AdminChangeUserLog;
import com.jumi.microservice.domain.MessageTemplate;
import com.jumi.microservice.dto.request.JmUserUpdateRequest;
import com.jumi.microservice.entity.JmJpushUser;
import com.jumi.microservice.entity.JmUser;
import com.jumi.microservice.entity.JmUserInvite;
import com.jumi.microservice.entity.JmUserTeam;
import com.jumi.microservice.entity.vo.JmUserSetVo;
import com.jumi.microservice.enums.*;
import com.jumi.microservice.mapper.JmUserMapper;
import com.jumi.microservice.mq.SendMessageHandler;
import com.jumi.microservice.service.IJmJpushUserService;
import com.jumi.microservice.service.IJmUserInviteService;
import com.jumi.microservice.service.IJmUserService;
import com.jumi.microservice.service.IJmUserTeamService;
import com.jumi.microservice.util.NameGen;
import com.jumi.microservice.util.PasswordUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * <p>
 * 会员表 服务实现类
 * </p>
 *
 * @author 要挑客户管理团队
 * @since 2020-07-14
 */
@Service
@Transactional
public class JmUserServiceImpl extends ServiceImpl<JmUserMapper, JmUser> implements IJmUserService {

    @Resource
    JmUserMapper mapper;

    @Resource
    IJmUserTeamService teamService;

    @Resource
    IJmUserInviteService inviteService;

    @Resource
    RedisService redisService;

    @Resource
    IJmJpushUserService jpushUserService;

    @Resource
    SendMessageHandler sendMessageHandler;

    @Override
    @AdminChangeUserLog(value = "更新会员")
    @Transactional
    public ResponseResult updateUser(JmUserUpdateRequest request, JmUser user) {
        if (ObjectUtil.isNull(user)) {
            user = getById(request.getId());
        }
        if (user == null) {
            return ResponseResult.error("无此用户!");
        }
        //在更新用户时，不允许修改用户等级.
        Level level = user.getLevel();
        String passwd = user.getPasswd();//用户数据库存储的密码;
        String reqPasswd = request.getPasswd();//前端传来的用户密码;
        String paypwd = user.getPaypwd();
        String reqPaypwd = request.getPaypwd();
        String updateMobile = request.getMobile();
        String mobile = user.getMobile();

        BeanUtils.copyProperties(request, user);
        String updateRemark = "普通更新";
        //将要更新的手机号;
        if (!StrUtil.equals(updateMobile, mobile)) {
            JmUser userByMobile = getUserByMobile(updateMobile);
            updateRemark = "修改手机号";
            if (ObjectUtil.isNotEmpty(userByMobile)) {
                return ResponseResult.error("此手机号已经被注册!");
            }
        }
        if (StrUtil.isNotEmpty(reqPasswd) && !StrUtil.equals(passwd, reqPasswd)) {
            //前端传来的密码与数据库存储密码不一致时，需要更新用户密码;
            user.setPasswd(PasswordUtils.passwd(reqPasswd));
        }
        if (StrUtil.isNotEmpty(reqPaypwd) && !StrUtil.equals(paypwd, reqPaypwd)) {
            //前端传来的密码与数据库存储密码不一致时，需要更新用户密码;
            user.setPaypwd(PasswordUtils.passwd(reqPaypwd));
        }
        user.setUpdateTime(LocalDateTime.now());
        user.setLevel(level);
        boolean update = updateById(user);
        AdminChangeUserLogAspect.setBack(updateRemark, user.getId());//添加用户更改日志
        String format = StrUtil.format("操作{}", update ? "成功！" : "失败！");
        return ResponseResult.success(format);
    }

    @Override
    @AdminChangeUserLog(value = "添加会员")
    @Transactional
    public void saveUser(JmUser jmUser) {
        save(jmUser);
        Integer id = jmUser.getId();
        jmUser = new JmUser();
        jmUser.setId(id);
        jmUser.setCode(StrUtil.format("YT{}", id + 20200808));
        updateById(jmUser);
        AdminChangeUserLogAspect.setBack("添加会员", jmUser.getId());
    }

    public void checkBindUser(JmUser user, JmUser bindUser, Integer bindType) {
        if (ObjectUtil.isNull(user)) {
            throw new BaseException(500, "上级手机号未注册!");
        }
        if (ObjectUtil.isNull(bindUser)) {
            throw new BaseException(500, "手机号未注册!");
        }

        if (user.getInviterId().equals(bindUser.getId())) {
            throw new BaseException(500, "不允许重复绑定!");
        }

        if (user.getId().equals(bindUser.getId())) {
            throw new BaseException(500, "不允许绑定自己!");
        }

        //注销的手机号不允许绑定,检查被绑定用户
        Status bindUserStatus = bindUser.getStatus();
        Status userStatus = user.getStatus();
        if (bindUserStatus.equals(Status.关闭) || userStatus.equals(Status.关闭)) {
            throw new BaseException(500, "用户已注销,不能绑定!");
        }

        JmUser temp = user;
        if (bindType.equals(1)) {
            //当绑定下级时，需要检查下级是否已经绑定过上级
            temp = bindUser;
            if (ObjectUtil.isNotNull(temp.getInviterId()) && !temp.getInviterId().equals(0)) {
                throw new BaseException(500, "此用户已经绑定上级!");
            }
            ;
        }
        //金牌推广商不允许修改,检查绑定用户
        if (ObjectUtil.equal(temp.getLevel(), Level.gold)) {
            throw new BaseException(500, "用户当前等级不支持绑定新上级!");
        }
    }

    public JmUser packUser(Object arg) {
        JmUser jmUser;
        if (arg.getClass().equals(JmUser.class)) {
            jmUser = (JmUser) arg;
        } else {
            jmUser = new JmUser();
            BeanUtils.copyProperties(arg, jmUser);
        }
        LocalDateTime now = LocalDateTime.now();
        if (ObjectUtil.isEmpty(jmUser.getName())) {
            jmUser.setName(NameGen.genName(jmUser.getMobile()));
        }
        jmUser.setCreateTime(now);
        jmUser.setBirthday(LocalDate.now());
        jmUser.setUpdateTime(now);
        jmUser.setLoginTime(now);
        jmUser.setLastLevelUpTime(now);
        jmUser.setStatus(Status.开启);
//        jmUser.setIsJumi(CommonEnum.否);
        jmUser.setAllowAuthorize(CommonEnum.是);
        jmUser.setAllowLogin(CommonEnum.是);
        jmUser.setAllowBuy(CommonEnum.是);
        jmUser.setAllowReport(CommonEnum.是);
        jmUser.setIsRealnameAuthentication(CommonEnum.否);
        jmUser.setIsIdentityAuthentication(CommonEnum.否);
        jmUser.setLevel(Level.user);
        jmUser.setOriginLevel(Level.user);
        jmUser.setRegisterTime(now);
        return jmUser;
    }


    @Override
    public JmUser checkUserExist(Integer memberId) {
        QueryWrapper wrapper = new QueryWrapper();
        wrapper.eq("id", memberId);
        JmUser one = getOne(wrapper);
        if (ObjectUtil.isEmpty(one)) {
            throw new BaseException("未查询到用户信息!");
        }
        return one;
    }

    @Override
    public JmUserSetVo findUserSetById(Integer memberId) {
        String registerId = ServletUtils.getStrHeader(JwtAdminInfo.jpushRegisterId);//极光推送Id
        JmUserSetVo vo = mapper.findUserSetById(memberId);
        vo.setJpushStatus(2);
        if (StringUtils.isNotBlank(registerId)) {
            JmJpushUser jpushUser = jpushUserService.getJgPushByRegisterId(registerId, memberId);
            if (ObjectUtil.isNotEmpty(jpushUser)) {
                vo.setJpushStatus(jpushUser.getStatus());
            }
        }
        return vo;
    }

    //绑定上级
    @AdminChangeUserLog("绑定上级")
    @Override
    @Transactional
    public ResponseResult bindUp(JmUser user, JmUser bindUser, SourceType sourceType) {
        AdminChangeUserLogAspect.setBack(StrUtil.format("{} 绑定上级 ->{}", user.getName(), bindUser.getName()), user.getId());
        doBind(user, bindUser, sourceType);
        try {

            MessageTemplate template = sendMessageHandler.getMessageTemplate(MessageSendType.INVITE.getValue());
            String  mobile = user.getMobile().substring(0, 3) + "****" + user.getMobile().substring(7);
            String appMessageContent = StrUtil.format(template.getSmtAppContent(),mobile);
            String stationMessageContent = StrUtil.format(template.getSmtMessageContent(),mobile);;
            sendMessageHandler.sendMessage(appMessageContent, stationMessageContent, bindUser.getId(), MessageSendType.INVITE.getValue());
        } catch (Exception e) {
            log.error("消息发送失败!");
        }
        return ResponseResult.success();
    }

    //绑定下级
    @AdminChangeUserLog("绑定下级")
    @Override
    public ResponseResult bindDown(JmUser user, JmUser bindUser, SourceType sourceType) {
        AdminChangeUserLogAspect.setBack(StrUtil.format("{} 绑定下级 ->{}", user.getName(), bindUser.getName()), user.getId());
        doBind(bindUser, user, sourceType);
        return ResponseResult.success();
    }

    /**
     * 获取正常用户的数量
     *
     * @return
     */
    @Override
    public Integer getUserCountByNormal() {
        QueryWrapper wrapper = new QueryWrapper();
        wrapper.eq("allow_login", 1);
        wrapper.eq("status", 1);
        return count(wrapper);
    }

    @Override
    public JmUserUpdateRequest getUserInfoById(Integer id) {
        return mapper.getUserInfoById(id);
    }

    @Override
    public JmUser getUserByMobile(String mobile) {
        QueryWrapper wrapper = new QueryWrapper();
        wrapper.eq("mobile", mobile);
        return getOne(wrapper);
    }


    /**
     * @param user       下级
     * @param userSuper  上级
     * @param sourceType 类型
     * @return
     */
    @Transactional
    public void doBind(JmUser user, JmUser userSuper, SourceType sourceType) {
        Integer memberId = user.getId();
        Integer superId = userSuper.getId();//上级id
        JmUserInvite invite = getInvite(user, userSuper, sourceType);//封装invite对象
        JmUserTeam team = getTeam(user, userSuper, JoinType.直接邀请);//封装team对象

        List<JmUserTeam> userTeams = new ArrayList<>();//团队链
        userTeams.add(team);
        //查询 userSuper 的所有上级
        QueryWrapper teamW = new QueryWrapper();
        teamW.eq("uid", superId);
        teamW.eq("is_del", CommonEnum.否);
        teamW.select("team_uid");
        List<Integer> teamUid = teamService.listObjs(teamW, t -> Integer.valueOf(t.toString()));
        teamUid.add(-1);
        for (Integer id : teamUid) {
            if (memberId.equals(id)) {
                throw new BaseException("用户已存在于当前团队链中!");
            }
        }
        cleanBind(user);//清理下级的团队关系
        //查询所有上级用户信息
        QueryWrapper userW = new QueryWrapper();
        userW.in("id", teamUid);
        userW.select("id", "name");
        List<JmUser> users = list(userW);
        //构建我与绑定上级,所有上级的团队链
        users.stream().forEach(u -> userTeams.add(getTeam(user, u, JoinType.间接邀请)));
        //构建我的下级与绑定上级,所有上级的团队链
        //查询团队表,获得我的下级
        QueryWrapper wrapper = new QueryWrapper();
        wrapper.eq("team_uid", memberId);
        wrapper.eq("is_del", CommonEnum.否);
        wrapper.select("uid");
        //我的下级用户id
        List list = teamService.listObjs(wrapper, (Function<Object, Object>) o -> Integer.valueOf(o.toString()));
        list.add(-1);
        wrapper = new QueryWrapper();
        wrapper.select("id", "level", "name");
        wrapper.in("id", list);
        users.add(userSuper);//将本次绑定的上级也加入到列表中
        List<JmUser> myLowerUserInfo = list(wrapper);
        //将我的下级与所有上级建立关系
        for (JmUser jmUser : myLowerUserInfo) {
            //jmUser:我的下级
            for (JmUser up : users) {
                //up:我的上级
                userTeams.add(getTeam(jmUser, up, JoinType.间接邀请));
            }
        }

        inviteService.save(invite);
        //插入redis开始---
        redisService.setCacheMapValue("hash_member_invite", String.valueOf(memberId), String.valueOf(superId));
        String zSetKey = StrUtil.format("set_invite_id_{}", String.valueOf(superId));
        redisService.setZset(zSetKey, System.currentTimeMillis(), String.valueOf(memberId));
        //插入redis结束---
        teamService.saveBatch(userTeams);
        user.setInviterId(superId);
        user.setUpdateTime(LocalDateTime.now());
        updateById(user);
    }


    /**
     * @param user 下级
     */
    public void cleanBind(JmUser user) {
        /**
         * 查询下级是否已经绑定过了团队关系，将原有团队关系清理。
         */
        Integer id = user.getId();
        Integer inviterId = user.getInviterId();
        Long loginId = ServletUtils.getLoginId();
        String loginName = ServletUtils.getLoginName();
        //查询邀请关系表
        JmUserInvite invite = inviteService.getInviteByUid(id);
        //查询团队表,获得我的上级记录
        List<JmUserTeam> mySupers = teamService.getMySupers(id);
        //查询团队表,获得我的下级
        QueryWrapper wrapper = new QueryWrapper();
        wrapper.eq("team_uid", id);
        wrapper.eq("is_del", CommonEnum.否);
        wrapper.select("uid");
        //我的下级用户id
        List list = teamService.listObjs(wrapper, (Function<Object, Object>) o -> Integer.valueOf(o.toString()));
        list.add(-1);
        //查询我的下级与我的上级之间的记录
        List<Integer> mySuperIds = mySupers.stream().map(JmUserTeam::getTeamUid).collect(Collectors.toList());
        mySuperIds.add(-1);
        wrapper = new QueryWrapper();
        wrapper.eq("is_del", CommonEnum.否);
        wrapper.in("uid", list);
        wrapper.in("team_uid", mySuperIds);
        List<JmUserTeam> list1 = teamService.list(wrapper);//我的下级与我的上级记录

        //所有的上下级关系
        List<JmUserTeam> allTeam = new ArrayList<>(mySupers.size() + list1.size());
        allTeam.addAll(mySupers);
        allTeam.addAll(list1);
        //清理
        if (ObjectUtil.isNotEmpty(invite)) {
            invite.setIsDel(CommonEnum.是);
            invite.setUpdateTime(LocalDateTime.now());
            inviteService.updateById(invite);
        }
        if (ObjectUtil.isNotEmpty(allTeam)) {
            allTeam.stream().forEach(s -> {
                s.setIsDel(CommonEnum.是);
                s.setUpdateTime(LocalDateTime.now());
                s.setAdminId(loginId);
                s.setAdminName(loginName);
            });
            teamService.updateBatchById(allTeam);
        }
        //清理redis的团队链关系
        String zSetKey = StrUtil.format("set_invite_id_{}", inviterId);
        redisService.remZset(zSetKey, id.toString());
    }


    public JmUserInvite getInvite(JmUser user, JmUser bindUser, SourceType sourceType) {
        Long loginId = ServletUtils.getLoginId();
        String loginName = ServletUtils.getLoginName();
        JmUserInvite invite = new JmUserInvite();
        invite.setUid(user.getId());
        invite.setInviteUid(bindUser.getId());
        invite.setSourceType(sourceType);
        invite.setInviteCode(bindUser.getCode());
        invite.setBackup(StrUtil.format("id:{},name:{} 绑定上级为: id:{},name:{}"
                , user.getId(), user.getName(), bindUser.getId(), bindUser.getName()));
        invite.setIsDel(CommonEnum.否);
        invite.setCreateTime(LocalDateTime.now());
        invite.setUpdateTime(LocalDateTime.now());
        invite.setAdminId(loginId);
        invite.setAdminName(loginName);
        return invite;
    }

    ;

    public JmUserTeam getTeam(JmUser user, JmUser bindUser, JoinType joinType) {
        Long loginId = ServletUtils.getLoginId();
        String loginName = ServletUtils.getLoginName();
        JmUserTeam team = new JmUserTeam();
        team.setUid(user.getId());
        team.setLevel(user.getLevel());
        team.setTeamUid(bindUser.getId());
        team.setJoinType(joinType);
        team.setJoinTime(LocalDateTime.now());
        team.setBackup(StrUtil.format("id:{},name:{} 绑定上级为: id:{},name:{}"
                , user.getId(), user.getName(), bindUser.getId(), bindUser.getName()));
        team.setIsDel(CommonEnum.否);
        team.setCreateTime(LocalDateTime.now());
        team.setUpdateTime(LocalDateTime.now());
        team.setAdminId(loginId);
        team.setAdminName(loginName);
        return team;
    }

    @Override
    public Page getPage(Page page, Wrapper wrapper) {
        return mapper.userPage(page, wrapper);
    }

}
